﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MacomberMapSystem.Common.CIM;
using System.IO;
using System.Threading;
using MacomberMapSystem.Common.Database;
using System.Xml;

namespace MacomberMapSystem.Common.User_Interfaces.ModelSampler
{
    /// <summary>
    /// This class creates a sample model based on one element
    /// </summary>
    public partial class frmModelSampler : Form
    {
        #region Variable declarations
        /// <summary>Our base element</summary>
        private CIM_Element Elem;

        /// <summary>Our element type mapping</summary>
        private SortedDictionary<String, clsSamplerLinkage> ElemTypeMapping = new SortedDictionary<string, clsSamplerLinkage>();

        /// <summary>The collection of elements that have been written out</summary>
        private Dictionary<CIM_RdfID, CIM_Link> OutgoingElements = new Dictionary<CIM_RdfID, CIM_Link>();

        #endregion

        /// <summary>
        /// Initialize a new sampler form
        /// </summary>
        /// <param name="Elem"></param>
        /// <param name="OtherElements"></param>
        private frmModelSampler(CIM_Element Elem, params CIM_Element[] OtherElements)
        {
            InitializeComponent();
            this.Elem = Elem;
            this.Text = "Model sampler: " + Elem.ElemType + " " + Elem.LineName;
            lvElements.HideSelection = false;
            //Build our list of types to handle
            List<String> ToInclude = new List<string>();
            foreach (CIM_Element Elem2 in OtherElements)
                if (!ToInclude.Contains(Elem2.TypeName))
                    ToInclude.Add(Elem2.TypeName);

            lblRootElem.Text = "Root Element: " + Elem.TypeName + " " + Elem.LineName;

            //Write out our temp model
            using (StreamWriter sW = new StreamWriter(@"c:\users\mlegatt\desktop\CIM_" + Elem.TypeName.Replace(':', '_') + "_" + Elem.LineName + ".xml"))
            {
                sW.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                sW.WriteLine("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:cim=\"http://iec.ch/TC57/2006/CIM-schema-cim10#\" xmlns:spc=\"http://www.siemens-ptd/SHIMM1.0#\" xmlns:etx=\"http://www.ercot.com/CIM11R0/2008/2.0/extension#\">");
                foreach (CIM_Element Elem2 in OtherElements)
                    sW.WriteLine(Elem2.XML);
                sW.WriteLine("</rdf:RDF>");
            }
            //Create our list of element types
            clsSamplerLinkage SelItem = null;
            foreach (String str in Elem.CIM.Types)
            {
                clsSamplerLinkage.AddLinkage(str, ElemTypeMapping);
                if (str == Elem.TypeName)
                    SelItem = ElemTypeMapping[str.Substring(str.IndexOf(':')+1)];
            }
            foreach (clsSamplerLinkage Link in ElemTypeMapping.Values)
                lvElements.Items.Add(Link);

            lvElements.FocusedItem = SelItem;
            SelItem.Focused = SelItem.Selected = true;
            SelItem.EnsureVisible();
           
        }

        


        /// <summary>
        /// Create our sampler dialog
        /// </summary>
        /// <param name="Elem"></param>
        /// <param name="OtherElements"></param>
        public static void CreateSample(CIM_Element Elem, params CIM_Element[] OtherElements)
        {
            using (frmModelSampler Sampler = new frmModelSampler(Elem, OtherElements))
                Sampler.ShowDialog();
                   
        }

        /// <summary>
        /// Enable our timer for tracking
        /// </summary>
        private void EnableTimer()
        {
            if (InvokeRequired)
                Invoke(new TimerDelegate(EnableTimer));
            else
                tmrUpdate.Enabled = true;
        }

        /// <summary>
        /// Disable our timer for tracking
        /// </summary>
        private void DisableTimer()
        {
            if (InvokeRequired)
                Invoke(new TimerDelegate(DisableTimer));
            else
            {
                tmrUpdate.Enabled = false;
                this.DialogResult = System.Windows.Forms.DialogResult.OK;
                this.Close();
            }
        }

        private delegate void TimerDelegate();

        /// <summary>
        /// Write out our file
        /// </summary>
        private void WriteFile(object state)
        {
            //Start by writing out our header information
            string TargetFileName = btnSelect.Tag.ToString();
            using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(Path.GetDirectoryName(TargetFileName), Path.GetFileNameWithoutExtension(TargetFileName) + "_settings.xml"), new UTF8Encoding(false)))
            {
                xW.WriteStartDocument();
                xW.WriteStartElement("Export");
                xW.WriteAttributeString("StartedOn", XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified));
                foreach (clsSamplerLinkage Link in ElemTypeMapping.Values)
                {
                    xW.WriteStartElement("ElementType");
                    xW.WriteAttributeString("TypeName", Link.TypeName);
                    xW.WriteAttributeString("Name", Link.Text);
                    foreach (clsSamplerLinkage OutLink in Link.OutgoingLinks)
                    {
                        xW.WriteStartElement("Outgoing");
                        xW.WriteAttributeString("TypeName", OutLink.TypeName);
                        xW.WriteEndElement();
                    }
                    foreach (clsSamplerLinkage InLink in Link.IncomingLinks)
                    {
                        xW.WriteStartElement("Incoming");
                        xW.WriteAttributeString("TypeName", InLink.TypeName);
                        xW.WriteEndElement();
                    }
                    xW.WriteEndElement();
                }
                xW.WriteEndElement();
                xW.WriteEndDocument();
            }



            this.Tag = OutgoingElements;
            EnableTimer();
            DateTime StartTime = DateTime.Now;

            using (StreamWriter sW = new StreamWriter(TargetFileName, false, new UTF8Encoding(false)))
            {
                sW.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                sW.WriteLine("<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:cim=\"http://iec.ch/TC57/2006/CIM-schema-cim10#\" xmlns:spc=\"http://www.siemens-ptd/SHIMM1.0#\" xmlns:etx=\"http://www.ercot.com/CIM11R0/2008/2.0/extension#\">");
                ParseElement(Elem, true,true,null, sW);
                sW.WriteLine("</rdf:RDF>");
            }
            DisableTimer();
            MessageBox.Show("Process completed in " + (DateTime.Now - StartTime).ToString() + ". " + OutgoingElements.Count.ToString("#,##0") + " elements exported.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);

            
            
            
        }

        /// <summary>
        /// Parse an element
        /// </summary>
        /// <param name="Elem"></param>
        /// <param name="Upward"></param>
        /// <param name="Downward"></param>
        /// <param name="ElemTypes"></param>
        /// <param name="sW"></param>
        private void ParseElement(CIM_Element Elem,bool Upward, bool Downward, Dictionary<String, bool> ElemTypes, StreamWriter sW)
        {                                 
            //Start by writing out our element and cache it
            sW.WriteLine(Elem.XML);
            Dictionary<CIM_RdfID, bool> OutgoingElements = (Dictionary<CIM_RdfID, bool>)this.Tag;
            OutgoingElements.Add(Elem.rdfID, true);


            //Now, parse around
            List<CIM_Link> ToParse = new List<CIM_Link>();
            clsSamplerLinkage Link = ElemTypeMapping[Elem.TypeName.Substring(Elem.TypeName.IndexOf(':') + 1)];
            foreach (CIM_Link cLink in Elem.InLinks)
                    foreach (clsSamplerLinkage eLink in Link.OutgoingLinks)
                        if (eLink.TypeName == cLink.TypeName)
                            ToParse.Add(cLink);
            foreach (CIM_Link cLink in Elem.OutLinks)
                    foreach (clsSamplerLinkage eLink in Link.IncomingLinks)
                        if (eLink.TypeName == cLink.TypeName)
                            ToParse.Add(cLink);
                    
             
        }


        /// <summary>
        /// Handle the selection button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            using (SaveFileDialog sFd = new SaveFileDialog())
            {
                sFd.Title = "Create new CIM model based on " + Elem.ElemType + " " + Elem.LineName;
                sFd.Filter = "CIM Model (*.xml)|*.xml";
                sFd.FileName = "CIM_" + Elem.ElemType + "_" + Elem.LineName + ".xml";
                if (sFd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    btnSelect.Text = Path.GetFileNameWithoutExtension(sFd.FileName);
                    btnSelect.Tag = sFd.FileName;                    
                }
            }
        }

        /// <summary>
        /// When our timer hits, update the display of # elements
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tmrUpdate_Tick(object sender, EventArgs e)
        {
            Dictionary<CIM_RdfID, bool> OutgoingElements = (Dictionary<CIM_RdfID, bool>)this.Tag;
            lblUpdate.Text = OutgoingElements.Count.ToString("#,##0");
        }

        /// <summary>
        /// Handle the OK button locally
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOk_Click(object sender, EventArgs e)
        {            

            ThreadPool.QueueUserWorkItem(WriteFile);
        }

      

        /// <summary>
        /// When our element is selected, handle accordingly
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvElements_SelectedIndexChanged(object sender, EventArgs e)
        {
            //Review our data dictionary to update accordingly
            tvTopology.Nodes.Clear();
            if (lvElements.SelectedItems.Count != 1)
                return;
            clsSamplerLinkage Link = lvElements.SelectedItems[0] as clsSamplerLinkage;

            //Add our outgoing links            
            Byte TypeByte = (Byte)Elem.CIM.Types.IndexOf(Link.TypeName);


            List<Byte> FoundTypes;            
            if (Elem.CIM is Common.CIM.Direct.CIM_DirectModel)
            if ((Elem.CIM as Common.CIM.Direct.CIM_DirectModel).TypeToTypes.TryGetValue(TypeByte, out FoundTypes))
            {
                TreeNode Outgoing = tvTopology.Nodes.Add("Outgoing");                
                foreach (Byte TargetByte in FoundTypes)
                {
                    string TargetType = Elem.CIM.Types[TargetByte].Split(':')[1];
                    TreeNode TN = Outgoing.Nodes.Add(TargetType);
                    TN.Tag = ElemTypeMapping[TargetType];
                    foreach (clsSamplerLinkage Out in Link.IncomingLinks)
                        if (Out.Text == TargetType)
                            TN.Checked = true;
                    TN.EnsureVisible();
                }
            }

            if (Elem.CIM is Common.CIM.Direct.CIM_DirectModel)
            if ((Elem.CIM as Common.CIM.Direct.CIM_DirectModel).TypeFromTypes.TryGetValue(TypeByte, out FoundTypes))
            {
                TreeNode Incoming = tvTopology.Nodes.Add("Incoming");
                foreach (Byte TargetByte in FoundTypes)
                {
                    string TargetType = Elem.CIM.Types[TargetByte].Split(':')[1];
                    TreeNode TN = Incoming.Nodes.Add(TargetType);
                    TN.Tag = ElemTypeMapping[TargetType];
                    foreach (clsSamplerLinkage Out in Link.OutgoingLinks)
                        if (Out.Text == TargetType)
                            TN.Checked = true;
                    TN.EnsureVisible();
                }                
            }
            tvTopology.Sort();
        }

        /// <summary>
        /// Handle our node check
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tvTopology_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Node.Parent == null)
                foreach (TreeNode TN in e.Node.Nodes)
                    TN.Checked = e.Node.Checked;
            else if (e.Node.Parent.Text == "Incoming")
                    if (e.Node.Checked)
                        (e.Node.Tag as clsSamplerLinkage).AddIncomingLink(lvElements.SelectedItems[0] as clsSamplerLinkage, true);
                    else
                        (e.Node.Tag as clsSamplerLinkage).RemoveIncomingLink(lvElements.SelectedItems[0] as clsSamplerLinkage, true);
                else if (e.Node.Parent.Text == "Outgoing")
                    if (e.Node.Checked)
                        (e.Node.Tag as clsSamplerLinkage).AddOutgoingLink(lvElements.SelectedItems[0] as clsSamplerLinkage,true);
                    else
                        (e.Node.Tag as clsSamplerLinkage).RemoveOutgoingLink(lvElements.SelectedItems[0] as clsSamplerLinkage, true);

        }

        /// <summary>
        /// Handle a node double-click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tvTopology_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            lvElements.SelectedItems.Clear();
            if (e.Node.Parent != null)
            {
                clsSamplerLinkage SelItem = ElemTypeMapping[e.Node.Text];                
                SelItem.EnsureVisible();
                lvElements.FocusedItem = SelItem;
                SelItem.Focused = SelItem.Selected = true;
                
            }
        }

        /// <summary>
        /// Load in our element selections
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLoad_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog oFd = new OpenFileDialog())
            {
                oFd.Title = "Select an element selection file...";
                oFd.Filter = "Element selection file (*.xml)|*.xml";
                if (oFd.ShowDialog() != DialogResult.OK)
                    return;
                //Now, import our file
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(oFd.FileName);
                foreach (XmlElement xElem in xDoc.DocumentElement.ChildNodes)
                {
                    clsSamplerLinkage Link = ElemTypeMapping[xElem.Attributes["Name"].Value];
                    List<clsSamplerLinkage> OutLinks = new List<clsSamplerLinkage>();
                    List<clsSamplerLinkage> InLinks = new List<clsSamplerLinkage>();
                    foreach (XmlElement xCh in xElem.ChildNodes)
                        if (xCh.Name == "Outgoing")
                            OutLinks.Add(ElemTypeMapping[xCh.Attributes["TypeName"].Value.Split(':')[1]]);
                        else if (xCh.Name == "Incoming")
                            InLinks.Add(ElemTypeMapping[xCh.Attributes["TypeName"].Value.Split(':')[1]]);

                    Link.OutgoingLinks = OutLinks.ToArray();
                    Link.IncomingLinks = InLinks.ToArray();
                }
            }
        }
    }
}
